home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 11 / Mac Magazin and MacEasy Magazine CD - Issue 11.iso / Sharewarebibliothek / Entwickler / WASTE 1.1 C / WELowLevelEditing.c < prev    next >
Text File  |  1995-05-18  |  28KB  |  1,020 lines

  1. // { WASTE PROJECT: }
  2. // { Low-Level Editing Routines }
  3.  
  4. // { Copyright © 1993-1995 Marco Piovanelli }
  5. // { All Rights Reserved }
  6.  
  7. #include "WASTEIntf.h"
  8.  
  9. pascal Boolean _WEIsWordRange(long rangeStart, long rangeEnd, WEHandle hWE)
  10. {
  11.     // { _WEIsWordRange returns TRUE if the specified range is a word range, }
  12.     // { i.e. if it would be possible to select it by double-clicking and (optionally) dragging. }
  13.  
  14.     long wordStart, wordEnd;
  15.  
  16.     // { determine if rangeStart is at the beginning of a word }
  17.     WEFindWord(rangeStart, kLeadingEdge, &wordStart, &wordEnd, hWE);
  18.     if (rangeStart == wordStart) 
  19.     {
  20.  
  21.         // { determine if rangeEnd is at the end of a word }
  22.         WEFindWord(rangeEnd, kTrailingEdge, &wordStart, &wordEnd, hWE);
  23.         return (rangeEnd == wordEnd);
  24.     }
  25.     return false;
  26. } // { _WEIsWordRange }
  27.  
  28. pascal Boolean _WEIsPunct(long offset, WEHandle hWE)
  29. {
  30.     short cType;
  31.     
  32.     cType = WECharType(offset, hWE);
  33.     if ((cType & smcTypeMask) == smCharPunct) 
  34.     {
  35.         cType = cType & smcClassMask;
  36.         if (cType == smPunctNormal || cType == smPunctBlank) 
  37.             return true;
  38.     }
  39.     return false;
  40. }  // { _WEIsPunct }
  41.  
  42. pascal void _WEIntelligentCut(long *rangeStart, long *rangeEnd, WEHandle hWE)
  43. {
  44.  
  45.     // { _WEIntelligentCut is called by other WASTE routines to determine the actual }
  46.     // { range to be deleted when weFIntCutAndPaste is enabled. }
  47.     // { On entry, rangeStart and rangeEnd specify the selection range visible to the user. }
  48.     // { On exit, rangeStart and rangeEnd specify the actual range to be removed. }
  49.  
  50.     // { do nothing if the intelligent cut-and-paste feature is disabled }
  51.     if (!BTST((*hWE)->flags, weFIntCutAndPaste)) 
  52.         return;
  53.  
  54.     // { intelling cut-&-paste rules should be applied only to word ranges... }
  55.     if (_WEIsWordRange(*rangeStart, *rangeEnd, hWE) == false) 
  56.         return;
  57.  
  58.     // { ...without punctuation characters at the beginning or end }
  59.     if (_WEIsPunct(*rangeStart, hWE)) 
  60.         return;
  61.     if (_WEIsPunct(*rangeEnd - 1, hWE)) 
  62.         return;
  63.  
  64.     // { if the character preceding the selection range is a space, discard it }
  65.     if (WEGetChar(*rangeStart - 1, hWE) == kSpace) 
  66.         *rangeStart = *rangeStart - 1;
  67.     // { else, if the character following the selection range is a space, discard it }
  68.     else if (WEGetChar(*rangeEnd, hWE) == kSpace)
  69.         *rangeEnd = *rangeEnd + 1;
  70.  
  71. } // { _WEIntelligentCut }
  72.  
  73. pascal short _WEIntelligentPaste(long rangeStart, long rangeEnd, WEHandle hWE)
  74. {
  75.     short retval;
  76.     
  77.     // { _WEIntelligentPaste is called by other WASTE routines to determine whether }
  78.     // { an additional space character should be added (before or after) after inserting }
  79.     // { new text (usually from the Clipboard or from a drag). }
  80.  
  81.     retval = weDontAddSpaces;
  82.  
  83.     // { do nothing unless the intelligent cut-and-paste feature is enabled }
  84.     if (!BTST((*hWE)->flags, weFIntCutAndPaste)) 
  85.         return retval;
  86.  
  87.     // { extra spaces will be added only if the pasted text looks like a word range, }
  88.     // { without punctuation characters at the beginning or at the end }
  89.     if (_WEIsPunct(rangeStart, hWE))
  90.         return retval;
  91.     if (_WEIsPunct(rangeEnd - 1, hWE))
  92.         return retval;
  93.  
  94.     // { if the character on the left of the pasted text is a punctuation character }
  95.     // { and the character on the right isn't,  add a space on the right, and vice versa }
  96.     if (_WEIsPunct(rangeStart - 1, hWE))
  97.     {
  98.         if (_WEIsPunct(rangeEnd, hWE) == false) 
  99.             retval = weAddSpaceOnRightSide;
  100.     }
  101.     else if (_WEIsPunct(rangeEnd, hWE))
  102.         retval = weAddSpaceOnLeftSide;
  103.  
  104.     return retval;
  105. } // { _WEIntelligentPaste }
  106.  
  107. pascal OSErr _WEInsertRun(long runIndex, long offset, long styleIndex, WEPtr pWE)
  108. {
  109.  
  110.     // { Insert a new element in the style run array, at the specified runIndex position. }
  111.     // { The new element consists of the pair <offset, styleIndex>. }
  112.  
  113.     RunArrayElement element;
  114.     OSErr err;
  115.  
  116.     // { prepare the element record to be inserted in the array }
  117.     element.runStart = offset;
  118.     element.styleIndex = styleIndex;
  119.  
  120.     // { do the insertion }
  121.     err = _WEInsertSlot((Handle)pWE->hRuns, (Ptr)&element, runIndex + 1, sizeof(element));
  122.     if (err != noErr) 
  123.         return err;
  124.  
  125.     // { increment style run count }
  126.     pWE->nRuns = pWE->nRuns + 1;
  127.  
  128.     // { increment the reference count field of the style table element }
  129.     // { referenced by the newly inserted style run }
  130.     (*pWE->hStyles)[styleIndex].refCount = (*pWE->hStyles)[styleIndex].refCount + 1;
  131.  
  132.     return noErr;
  133. } // { _WEInsertRun }
  134.  
  135. pascal OSErr _WERemoveRun(long runIndex, WEPtr pWE)
  136. {
  137.     // { remove the specified element from the style run array }
  138.  
  139.     long styleIndex;
  140.     OSErr retval;
  141.     
  142.     styleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  143.  
  144.     // { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  145.     retval = _WERemoveSlot((Handle)pWE->hRuns, runIndex, sizeof(RunArrayElement));
  146.  
  147.     // { decrement style run count }
  148.     pWE->nRuns = pWE->nRuns - 1;
  149.  
  150.     // { decrement the reference count field of the style table element }
  151.     // { that was referenced by the style run we have just removed }
  152.     (*pWE->hStyles)[styleIndex].refCount =(*pWE->hStyles)[styleIndex]. refCount - 1;
  153.  
  154.     // { dispose of embedded object, if any }
  155.     if ((*pWE->hStyles)[styleIndex].refCount == 0) 
  156.         if (_WEFreeObject((WEObjectDescHandle)((*pWE->hStyles)[styleIndex].info.runStyle.tsObject))
  157.             != noErr) 
  158.             { ; }
  159.     
  160.     return retval;
  161. } // { _WERemoveRun }
  162.  
  163. pascal void _WEChangeRun(long runIndex, long newStyleIndex, Boolean keepOld, WEPtr pWE)
  164. {
  165.     // { change the styleIndex field of the specified element of the style run array }
  166.  
  167.     long oldStyleIndex;
  168.     WEObjectDescHandle hObjectDesc;
  169.  
  170.     // { do the change }
  171.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  172.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  173.     
  174.     // { increment the reference count field of the new style table element }
  175.     (*pWE->hStyles)[newStyleIndex].refCount = (*pWE->hStyles)[newStyleIndex].refCount + 1;
  176.     hObjectDesc = (WEObjectDescHandle)((*pWE->hStyles)[newStyleIndex].info.runStyle.tsObject);
  177.     
  178.     // { decrement the reference count field of the old style table element }
  179.     (*pWE->hStyles)[oldStyleIndex].refCount = (*pWE->hStyles)[oldStyleIndex].refCount - 1;
  180.  
  181.     // { dispose of embedded object, if any, unless it is again referenced in the new style }
  182.     if (((*pWE->hStyles)[oldStyleIndex].refCount == 0) && (keepOld == false))
  183.         if ((WEObjectDescHandle)((*pWE->hStyles)[oldStyleIndex].info.runStyle.tsObject) != hObjectDesc)
  184.             if (_WEFreeObject((WEObjectDescHandle)((*pWE->hStyles)[oldStyleIndex].info.runStyle.tsObject)) != noErr) 
  185.                 { ; }
  186.  
  187. } // { _WEChangeRun }
  188.  
  189. pascal OSErr _WENewStyle(WERunAttributes *ts, long *styleIndex, WEPtr pWE)
  190. {
  191.  
  192.     // { given the specified WERunAttributes record, find the corresponding entry }
  193.     // { in the style table (create a new entry if necessary), and return its index }
  194.  
  195.  
  196.     StyleTablePtr pTable;
  197.     StyleTableElement element;
  198.     long index, unusedIndex;
  199.     OSErr err;
  200.  
  201.     // _WENewStyle = noErr;
  202.     pTable = *pWE->hStyles;
  203.  
  204.     // { see if the given style already exists in the style table }
  205.     // { while scanning the table, also remember the position of the first unused style, if any }
  206.     index = 0;
  207.     unusedIndex = -1;
  208.     while (index < pWE->nStyles)
  209.     {
  210.         // { check for entries which aren't referenced and can be recycled }
  211.         if (pTable[index].refCount == 0) 
  212.         {
  213.             unusedIndex = index;
  214.         }
  215.  
  216.         // { perform a bitwise comparison between the current element and the specified style }
  217.         if (_WEBlockCmp((Ptr)&pTable[index].info, (Ptr)ts, sizeof(WERunAttributes)))
  218.         { 
  219.             *styleIndex = index;        // { found: style already present }
  220.             return noErr;
  221.         }
  222.  
  223.         index = index + 1;
  224.     } // { while }
  225.  
  226.     // { the specified style doesn't exist in the style table }
  227.     // { see if we can recycle an unused entry }
  228.     if (unusedIndex >= 0) 
  229.     {
  230.         index = unusedIndex;
  231.         pTable[index].info = *ts;
  232.     }
  233.     else
  234.     {
  235.         // { no reusable entry: we have to append a new element to the table }
  236.         element.refCount = 0;
  237.         element.info = *ts;
  238.         err = _WEInsertSlot((Handle)pWE->hStyles, (Ptr)&element, index, sizeof(element));
  239.         if (err != noErr) 
  240.         {
  241.             return err;
  242.         }
  243.  
  244.         // { update style count in the WE record }
  245.         pWE->nStyles = index + 1;
  246.     }
  247.  
  248.     // { return the index to the new element }
  249.     *styleIndex = index;
  250.  
  251.     return noErr;
  252. } // { _WENewStyle }
  253.  
  254. pascal OSErr _WERedraw(long rangeStart, long rangeEnd, WEHandle hWE)
  255. {
  256.     // { the WE record is guaranteed to be already locked }
  257.     WEPtr pWE;
  258.     LineArrayPtr pLines;
  259.     long startLine, endLine;
  260.     long oldTextHeight, newTextHeight;
  261.     LongRect r;
  262.     Rect viewRect, updateRect;
  263.     RgnHandle saveClip;
  264.     GrafPtr savePort;
  265.     OSErr err;
  266. #ifdef WEREDRAW_SPEED
  267.     LongRect scrollRect;
  268.     RgnHandle updateRgn,
  269.               utilRgn;
  270. #endif
  271.     
  272.     pWE = *hWE;
  273.  
  274.     // { do nothing if recalculation has been inhibited }
  275.     if (!BTST(pWE->flags, weFInhibitRecal)) 
  276.     {
  277.         // { hide the caret }
  278. #ifdef WEREDRAW_SPEED
  279.         BCLR(pWE->flags, weFCaretVisible);
  280. #else
  281.         if (BTST(pWE->flags, weFCaretVisible))
  282.         { 
  283.             _WEBlinkCaret(hWE);
  284.         }
  285. #endif
  286.  
  287.         // { remember total text height }
  288.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  289.  
  290.         // { find line range affected by modification }
  291.         startLine = WEOffsetToLine(rangeStart, hWE);
  292.         endLine = WEOffsetToLine(rangeEnd, hWE);
  293.  
  294.         // { recalculate line breaks starting from startLine }
  295.         err = _WERecalBreaks(&startLine, &endLine, hWE);
  296.         if (err != noErr) 
  297.         {
  298.             goto cleanup;
  299.         }
  300.  
  301.         // { recalculate slops }
  302.         _WERecalSlops(startLine, endLine, hWE);
  303.  
  304.         // { calculate new total text height }
  305.         newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  306.  
  307.         // { calculate the rectangle to redraw (in long coordinates) }
  308.         r.left = -SHRT_MAX;
  309.         r.right = SHRT_MAX;
  310.         pLines = *pWE->hLines;
  311.         r.top = pLines[startLine].lineOrigin;
  312.  
  313. #ifdef WEREDRAW_SPEED
  314.         // { if total text height hasn't changed, it's enough to redraw lines up to endLine }
  315.         // { otherwise we must redraw all lines from startLine on }
  316.  
  317.         if (endLine < pWE->nLines - 1)
  318.              r.bottom = pLines[endLine + 1].lineOrigin;
  319.         else
  320.             r.bottom = newTextHeight;
  321.         WEOffsetLongRect(&r, 0, pWE->destRect.top);
  322.  
  323.         if (newTextHeight == oldTextHeight)
  324.             WELongRectToRect(&r, &updateRect);
  325.         else
  326.         {
  327.             /*      Instead of scrolling the lines below the deleted text up by redrawing them,
  328.              *      use scroll bits to move the displayed text up.
  329.              */
  330.  
  331.             scrollRect = pWE->viewRect;
  332.             if (newTextHeight > oldTextHeight)
  333.                 scrollRect.top = pLines[startLine + 1].lineOrigin + pWE->destRect.top;
  334.             else
  335.                 scrollRect.top = pLines[startLine].lineOrigin + pWE->destRect.top;
  336.             WELongRectToRect(&scrollRect, &updateRect);
  337.             updateRgn = NewRgn();
  338.             ScrollRect(&updateRect, 0, newTextHeight - oldTextHeight, updateRgn);
  339.  
  340.             /*      Redraw the exposed region (caused by a scroll up)       */
  341.  
  342.             WELongRectToRect(&r, &updateRect);
  343.             utilRgn = NewRgn();
  344.             RectRgn(utilRgn, &updateRect);
  345.             DiffRgn(updateRgn, utilRgn, updateRgn);
  346.             DisposeRgn(utilRgn);
  347.             WEUpdate(updateRgn, hWE);
  348.             DisposeRgn(updateRgn);
  349.         }
  350. #else
  351.         // { if total text height hasn't changed, it's enough to redraw lines up to endLine }
  352.         // { otherwise we must redraw all lines from startLine on }
  353.         if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1)) 
  354.         {
  355.             r.bottom = pLines[endLine + 1].lineOrigin;
  356.         }
  357.         else if (newTextHeight < oldTextHeight) 
  358.         {
  359.             r.bottom = oldTextHeight;
  360.         }
  361.         else
  362.         {
  363.             r.bottom = newTextHeight;
  364.         }
  365.         
  366.         WEOffsetLongRect(&r, 0, pWE->destRect.top);
  367.  
  368.         // { calculate the intersection between this rectangle and the view rectangle }
  369.         WELongRectToRect(&r, &updateRect);
  370. #endif
  371.         WELongRectToRect(&pWE->viewRect, &viewRect);
  372.  
  373.         if (SectRect(&updateRect, &viewRect, &updateRect)) 
  374.         {
  375.             // { set up the port and the clip region }
  376.             GetPort(&savePort);
  377.             SetPort(pWE->port);
  378.  
  379.             // { set the clip region to updateRect }
  380.             saveClip = NewRgn();
  381.             GetClip(saveClip);
  382.             ClipRect(&updateRect);
  383.  
  384.             // { we only really need to redraw the visible lines }
  385.             startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  386.             endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  387.  
  388.             // { redraw the lines (pass TRUE in the doErase parameter) }
  389.             _WEDrawLines(startLine, endLine, true, hWE);
  390.  
  391.             // { erase the portion of the update rectangle below the last line (if any) }
  392.             pLines = *pWE->hLines;
  393.             updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  394.             if (updateRect.top < updateRect.bottom) 
  395.             {
  396.                 EraseRect(&updateRect);
  397.             }
  398.  
  399.             // { restore the clip region }
  400.             SetClip(saveClip);
  401.             DisposeRgn(saveClip);
  402.  
  403.             // { restore the port }
  404.             SetPort(savePort);
  405.  
  406.             // { redraw the caret or the selection range }
  407.             if (pWE->selStart < pWE->selEnd) 
  408.             {
  409.                 _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  410.             }
  411.             else
  412.             {
  413.                 _WEBlinkCaret(hWE);
  414.             }
  415.         } // { if SectRect }
  416.  
  417.         // { scroll the selection range into view }
  418.         WESelView(hWE);
  419.     } // { if recal not inhibited }
  420.  
  421.     // { clear result code }
  422.     err = noErr;
  423.  
  424. cleanup:
  425.     // { return result code }
  426.     return err;
  427. } // { _WERedraw }
  428.  
  429. /*
  430. pascal OSErr WECalText(WEHandle hWE)
  431. {
  432.     long startLine, endLine;
  433.     Boolean saveWELock;
  434.     OSErr err;
  435.  
  436.     // { lock WE record }
  437.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  438.  
  439.     // { recalculate all line breaks }
  440.     startLine = 0;
  441.     endLine = LONG_MAX;
  442.     err = _WERecalBreaks(&startLine, &endLine, hWE);
  443.  
  444.     // { recalculate line slops }
  445.     if (err == noErr) 
  446.     {
  447.         _WERecalSlops(startLine, endLine, hWE);
  448.     }
  449.  
  450.     // { unlock the WE record }
  451.     _WESetHandleLock((Handle)hWE, saveWELock);
  452.  
  453.     // { return result code }
  454.     return err;
  455. }
  456. */
  457.  
  458. pascal OSErr WECalText(WEHandle hWE)
  459. {
  460.     return _WERedraw(0, 0x7fffffff, hWE);
  461. }
  462.  
  463. pascal OSErr _WESetStyleRange(long rangeStart, long rangeEnd, short mode, WETextStyle *ts, WEHandle hWE)
  464. {
  465.     // { alter the style attributes of the specified text range according to ts and mode }
  466.     // { the WE record is guaranteed to be already locked }
  467.  
  468.     WEPtr pWE;
  469.     RunArrayHandle hRuns;
  470.     long offset;
  471.     long runIndex;
  472.     long oldStyleIndex, newStyleIndex;
  473.     WERunInfo runInfo;
  474.     short temp;
  475.     char continuousStyles;
  476.     OSErr err;
  477.  
  478. #ifdef WASTE_DEBUG
  479.         _WEAssert (rangeStart < rangeEnd, "\pbad style range");
  480. #endif
  481.  
  482.     pWE = *hWE;
  483.     hRuns = pWE->hRuns;
  484.  
  485.     // { if mode contains weDoToggleFace, we need to determine which QuickDraw styles }
  486.     // { are continuous over the specified text range: those styles must be turned off }
  487.     if (BTST(mode, kModeToggleFace)) 
  488.     {
  489.         temp = weDoFace;
  490.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  491.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  492.     }
  493.     else
  494.     {
  495.         continuousStyles = 0;
  496.     }
  497.  
  498.     // { find the index to the first style run in the specified range }
  499.     offset = rangeStart;
  500.     runIndex = _WEOffsetToRun(offset, hWE);
  501.  
  502.     // { run thru all the style runs that encompass the selection range }
  503.     do
  504.     {
  505.         // { find style index for this run and retrieve corresponding style attributes }
  506.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  507.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  508.  
  509.         // { _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run: }
  510.         // { correct this anomaly (which is useful for other purposes, anyway) }
  511.         if (runInfo.runEnd > pWE->textLength) 
  512.         {
  513.             runInfo.runEnd = pWE->textLength;
  514.         }
  515.         
  516.         // { apply changes to existing style attributes as requested }
  517.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  518.  
  519.         // { recalculate font metrics, if necessary }
  520.         if ((mode & (weDoFont + weDoSize + weDoFace + weDoAddSize)) != 0) 
  521.         {
  522.             _WEFillFontInfo(pWE->port, &runInfo.runAttrs);
  523.         }
  524.         
  525.         // { get a style index for the new attributes }
  526.         err = _WENewStyle(&runInfo.runAttrs, &newStyleIndex, pWE);
  527.         if (err != noErr) 
  528.         {
  529.             goto cleanup;
  530.         }
  531.         
  532.         // { if offset falls on a style boundary and this style run has become identical }
  533.         // { to the previous one, merge the two runs together }
  534.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  535.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  536.         {
  537.             err = _WERemoveRun(runIndex, pWE);
  538.             if (err != noErr) 
  539.             {
  540.                 goto cleanup;
  541.             }
  542.             runIndex = runIndex - 1;
  543.         }
  544.  
  545.         // { style index changed? }
  546.         if (oldStyleIndex != newStyleIndex) 
  547.         {
  548.             // { if offset is in the middle of a style run, insert a new style run in the run array }
  549.             if (offset > runInfo.runStart) 
  550.             {
  551.                 err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE);
  552.                 if (err != noErr) 
  553.                 {
  554.                     goto cleanup;
  555.                 }
  556.                 runIndex = runIndex + 1;
  557.             }
  558.             else
  559.             {
  560.                 // { otherwise just change the styleIndex field of the current style run element }
  561.                 _WEChangeRun(runIndex, newStyleIndex, (rangeEnd < runInfo.runEnd), pWE);
  562.             }
  563.  
  564.             // { if specified range ends in the middle of a style run, insert yet another element }
  565.             if (rangeEnd < runInfo.runEnd) 
  566.             {
  567.                 err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE);
  568.                 if (err != noErr) 
  569.                 {
  570.                     goto cleanup;
  571.                 }
  572.             }
  573.         } // { if oldStyle != newStyle }
  574.  
  575.         // { go to next style run }
  576.         runIndex = runIndex + 1;
  577.         offset = runInfo.runEnd;
  578.  
  579.     } while (offset < rangeEnd);
  580.  
  581.     // { if the last style run ends exactly at the end of the specified range, }
  582.     // { see if we can merge it with the following style run }
  583.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) && 
  584.         ((*hRuns)[runIndex].styleIndex == newStyleIndex)) 
  585.     {
  586.         err = _WERemoveRun(runIndex, pWE);
  587.         if (err != noErr) 
  588.         {
  589.             goto cleanup;
  590.         }
  591.     }
  592.  
  593.     // { clear result code }
  594.     err = noErr;
  595.  
  596. cleanup:
  597.     // { return result code }
  598.     return err;
  599. } // { _WESetStyleRange }
  600.  
  601. pascal OSErr _WEApplyStyleScrap(long rangeStart, long rangeEnd, StScrpHandle styleScrap, WEHandle hWE)
  602. {
  603.     // { apply the given style scrap to the specified text range }
  604.     // { the WE record is guaranteed to be already locked }
  605.  
  606.     WEPtr pWE;
  607.     TEStyleScrapPeek pElement;
  608.     long runStart, runEnd;
  609.     short index, lastElement;
  610.     WETextStyle ts;
  611.     OSErr err;
  612.  
  613.     // _WEApplyStyleScrap = noErr;
  614.     pWE = *hWE;
  615.  
  616.     // { loop through each element of the style scrap }
  617.     lastElement = (*styleScrap)->scrpNStyles - 1;
  618.     for(index = 0; index<=lastElement; index++)
  619.     {
  620.         // { get a pointer to the current scrap element }
  621.         pElement = (TEStyleScrapPeek)&(*styleScrap)->scrpStyleTab[index];
  622.  
  623.         // { calculate text run to which this element is to be applied }
  624.         runStart = rangeStart + pElement->first.scrpStartChar;
  625.         if (index < lastElement) 
  626.         {
  627.             runEnd = rangeStart + pElement->second.scrpStartChar;
  628.         }
  629.         else
  630.         {
  631.             runEnd = rangeEnd;
  632.         }
  633.  
  634.         // { perform some range checking }
  635.         if (runEnd > rangeEnd) 
  636.         {
  637.             runEnd = rangeEnd;
  638.         }
  639.         if (runStart >= runEnd) 
  640.         {
  641.             continue;
  642.         }
  643.  
  644.         // { copy style to a local variable in case memory moves }
  645.         *(TextStyle *)&ts = pElement->first.scrpTEAttrs.runTEStyle;
  646.         // { apply the specified style to the range }
  647.         err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE);
  648.         if (err != noErr) 
  649.         {
  650.             return err;
  651.         }
  652.     }
  653.     return noErr;
  654. } // { _WEApplyStyleScrap }
  655.  
  656. pascal OSErr _WEApplySoup(long offset, Handle hSoup, WEHandle hWE)
  657. {
  658.     WESoupPtr pSoup;
  659.     long pSoupEnd;
  660.     WETextStyle ts;
  661.     Handle hObjectData;
  662.     long objectOffset;
  663.     Boolean saveWELock;
  664.     OSErr err;
  665.  
  666.     _WEBlockClr((Ptr)&ts, sizeof(ts));
  667.     hObjectData = nil;
  668.  
  669.     // { lock the WE record }
  670.     saveWELock = _WESetHandleLock((Handle)hWE, true);
  671.  
  672.     // { lock the soup in high heap }
  673.     HLockHi(hSoup);
  674.     pSoup = (WESoupPtr)*hSoup;
  675.     pSoupEnd = (long)pSoup + GetHandleSize(hSoup);
  676.  
  677.     // { loop through each object descriptor in the soup }
  678.     while((long)pSoup < pSoupEnd)
  679.     {
  680.         // { if soupDataSize is negative, this soup is a special type that we won't handle here }
  681.         if (pSoup->soupDataSize < 0)
  682.             continue;
  683.  
  684.         // { create a new relocatable block the hold the object data }
  685.         err = _WEAllocate(pSoup->soupDataSize, kAllocTemp, &hObjectData);
  686.         if (err != noErr) 
  687.             goto cleanup;
  688.  
  689.         // { copy the object data to this block }
  690.         BlockMoveData((Ptr)(pSoup + 1), *hObjectData, pSoup->soupDataSize);
  691.  
  692.         // { create a new object out of the tagged data }
  693.         err = _WENewObject(pSoup->soupType, hObjectData, hWE, (WEObjectDescHandle *)&ts.tsObject);
  694.         if (err != noErr) 
  695.             goto cleanup;
  696.  
  697.         // { record a reference to the object descriptor in the style table }
  698.         objectOffset = pSoup->soupOffset + offset;
  699.         err = _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, &ts, hWE);
  700.         hObjectData = nil;
  701.         ts.tsObject = kNullObject;
  702.         if (err != noErr) 
  703.             goto cleanup;
  704.  
  705.         // { advance soup pointer }
  706.         pSoup = (WESoupPtr)((long)pSoup + sizeof(WESoup) + pSoup->soupDataSize);
  707.  
  708.     } // { while }
  709.  
  710.     // { clear result code }
  711.     err = noErr;
  712.  
  713. cleanup:
  714.     // { clean up }
  715.     HUnlock(hSoup);
  716.     _WEForgetHandle((Handle *)&ts.tsObject);
  717.     _WEForgetHandle(&hObjectData);
  718.  
  719.     // { unlock the WE record }
  720.     _WESetHandleLock((Handle)hWE, saveWELock);
  721.  
  722.     // { return result code }
  723.     return err;
  724.  
  725. } // { _WEApplySoup }
  726.  
  727. pascal void _WEBumpRunStart(long runIndex, long deltaRunStart, WEPtr pWE)
  728. {
  729.     // { add deltaLineStart to the lineStart field of all line records }
  730.     // { starting from lineIndex }
  731.  
  732.     long *pStart;
  733.     long nRuns;
  734.  
  735.     pStart = &(*pWE->hRuns)[runIndex].runStart;
  736.     nRuns = pWE->nRuns;
  737.  
  738.     // { loop through the style run array adjusting the runStart fields }
  739.     while (runIndex <= nRuns)
  740.     {
  741.         *pStart = *pStart + deltaRunStart;
  742.         pStart = (long *)((long)(pStart) + sizeof(RunArrayElement));
  743.         runIndex = runIndex + 1;
  744.     }
  745. } // { _WEBumpRunStart }
  746.  
  747. pascal OSErr _WERemoveRunRange(long rangeStart, long rangeEnd, WEHandle hWE)
  748. {
  749.     // { the range of text between rangeStart and rangeEnd is being deleted }
  750.     // { update the style run array (and the style table) accordingly }
  751.     // { the WE handle must be locked on entry }
  752.  
  753.     WEPtr pWE;
  754.     RunArrayPeek pRuns;
  755.     long startRun, endRun;
  756.     OSErr err;
  757.  
  758.     pWE = *hWE;
  759.  
  760.     // { find the index to the first and last style runs in the specified range }
  761.     startRun = _WEOffsetToRun(rangeStart, hWE);
  762.     endRun = _WEOffsetToRun(rangeEnd, hWE) - 1;
  763.  
  764.     // { remove all style runs between startRun and endRun }
  765.     while (endRun > startRun)
  766.     {
  767.         err = _WERemoveRun(endRun, pWE);
  768.         if (err != noErr) 
  769.         {
  770.             goto cleanup;
  771.         }
  772.         endRun = endRun - 1;
  773.     }
  774.     
  775.     // { move back all subsequent style runs }
  776.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  777.  
  778.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  779.     { 
  780.         pRuns = (RunArrayPeek)&(*pWE->hRuns)[endRun];
  781.         pRuns->second.runStart = rangeStart;
  782.     }
  783.  
  784.     // { remove the first style run if is has become zero length }
  785.     pRuns = (RunArrayPeek)&(*pWE->hRuns)[startRun];
  786.     if (pRuns->first.runStart == pRuns->second.runStart) 
  787.     {
  788.         err = _WERemoveRun(startRun, pWE);
  789.         if (err != noErr) 
  790.         {
  791.             goto cleanup;
  792.         }
  793.         startRun = startRun - 1;
  794.     }
  795.  
  796.     // { merge the first and last runs if they have the same style index }
  797.     if (startRun >= 0) 
  798.     {
  799.         pRuns = (RunArrayPeek)&(*pWE->hRuns)[startRun];
  800.         if (pRuns->first.styleIndex == pRuns->second.styleIndex) 
  801.         {
  802.             err = _WERemoveRun(startRun + 1, pWE);
  803.             if (err != noErr) 
  804.             {
  805.                 goto cleanup;
  806.             }
  807.         }
  808.     }
  809.     // { clear result code }
  810.     err = noErr;
  811.  
  812. cleanup:
  813.     // { return result code }
  814.     return err;
  815. } // { _WERemoveRunRange }
  816.  
  817. pascal void _WEBumpLineStart(long lineIndex, long deltaLineStart, WEPtr pWE)
  818. {
  819.     // { add deltaLineStart to the lineStart field of all line records }
  820.     // { starting from lineIndex }
  821.  
  822.     long *pStart;
  823.     long nLines;
  824.  
  825.     pStart = &(*pWE->hLines)[lineIndex].lineStart;
  826.     nLines = pWE->nLines;
  827.  
  828.     // { loop through the line array adjusting the lineStart fields }
  829.     while (lineIndex <= nLines)
  830.     {
  831.         *pStart = *pStart + deltaLineStart;
  832.         pStart = (long *)((long)pStart + sizeof(LineRec));
  833.         lineIndex = lineIndex + 1;
  834.     }
  835. } // { _WEBumpLineStart }
  836.  
  837. pascal OSErr _WERemoveLineRange(long rangeStart, long rangeEnd, WEHandle hWE)
  838. {
  839.     // { the range of text between rangeStart and rangeEnd is being deleted }
  840.     // { update the line array accordingly }
  841.     // { the WE handle must be locked on entry }
  842.  
  843.     WEPtr pWE;
  844.     long startLine, endLine;
  845.     OSErr err;
  846.     
  847.     // _WERemoveLineRange = noErr;
  848.     pWE = *hWE;
  849.  
  850.     // { remove all line records between rangeStart and rangeEnd }
  851.     startLine = WEOffsetToLine(rangeStart, hWE) + 1;
  852.     endLine = WEOffsetToLine(rangeEnd, hWE);
  853.     while (endLine >= startLine)
  854.     {
  855.         err = _WERemoveLine(endLine, pWE);
  856.         if (err != noErr) 
  857.         {
  858.             return err;
  859.         }
  860.         endLine = endLine - 1;
  861.     } // { while }
  862.  
  863.     // { update the lineStart field of all the line records that follow }
  864.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  865.  
  866.     return noErr;
  867. }
  868.  
  869. pascal OSErr _WEDeleteRange(long rangeStart, long rangeEnd, WEHandle hWE)
  870. {
  871.     // { used internally to delete a text range }
  872.     // { if saveNullStyle is TRUE, the first style in the range is saved in nullStyle }
  873.     // { the WE record is guaranteed to be already locked }
  874.  
  875.     WEPtr pWE;
  876.     WERunInfo runInfo;
  877.     long oldTextLength, newTextLength;
  878.     long pText;
  879.     OSErr err;
  880.  
  881.     pWE = *hWE;
  882.  
  883.     if (rangeEnd>pWE->textLength) rangeEnd = pWE->textLength;
  884.     
  885.     // { do nothing if the specified range is empty }
  886.     if (rangeStart == rangeEnd) 
  887.     {
  888.         goto cleanup1;
  889.     }
  890.     
  891.     // { save the first style in the specified range in nullStyle }
  892.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  893.     pWE->nullStyle = runInfo.runAttrs;
  894.     BSET(pWE->flags, weFUseNullStyle);
  895.  
  896.     // { special case: if we're deleting up to the end of the text, see whether }
  897.     // { there's an embedded object at the very end and remove it }
  898.     if (rangeEnd == pWE->textLength) 
  899.     {
  900.         WEGetRunInfo(rangeEnd - 1, &runInfo, hWE);
  901.         if (runInfo.runAttrs.runStyle.tsObject != kNullObject) 
  902.         {
  903.             runInfo.runAttrs.runStyle.tsObject = kNullObject;
  904.             err = _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, &runInfo.runAttrs.runStyle, hWE);
  905.             if (err != noErr) 
  906.                 goto cleanup2;
  907.         }
  908.     }
  909.  
  910.     // { remove all line records between rangeStart and rangeEnd }
  911.     err = _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  912.     if (err != noErr) 
  913.     {
  914.         goto cleanup2;
  915.     }
  916.     
  917.     // { remove all style runs between rangeStart and rangeEnd }
  918.     err = _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  919.     if (err != noErr) 
  920.     {
  921.         goto cleanup2;
  922.     }
  923.     
  924.     // { calculate old and new text length }
  925.     oldTextLength = pWE->textLength;
  926.     newTextLength = oldTextLength - (rangeEnd - rangeStart);
  927.  
  928.     // { move the end of the text backwards over the old selection range }
  929.     pText = (long)(*pWE->hText);
  930.     BlockMoveData((Ptr)pText + rangeEnd, (Ptr)pText + rangeStart, oldTextLength - rangeEnd);
  931.  
  932.     // { compact the text handle }
  933.     SetHandleSize((Handle)pWE->hText, newTextLength);
  934.     err = MemError();
  935.     if (err != noErr) 
  936.     {
  937.         goto cleanup2;
  938.     }
  939.  
  940.     // { update textLength field }
  941.     pWE->textLength = newTextLength;
  942.  
  943. cleanup1:
  944.     // { clear result code }
  945.     err = noErr;
  946.  
  947. cleanup2:
  948.     // { return result code }
  949.     return err;
  950. } // { _WEDeleteRange }
  951.  
  952. pascal OSErr _WEInsertText(long offset, Ptr textPtr, long textLength, WEHandle hWE)
  953. {
  954.     // { this routine assumes that the WE record is already locked }
  955.  
  956.     WEPtr pWE;
  957.     long oldTextLength, newTextLength;
  958.     long pInsPoint;
  959.     OSErr err;
  960.     short mode = 0;
  961.  
  962.     pWE = *hWE;
  963.  
  964.     // { do nothing if textLength is zero or negative }
  965.     if (textLength <= 0) 
  966.     {
  967.         goto cleanup1;
  968.     }
  969.     
  970.     // { calculate old and new length of text handle }
  971.     oldTextLength = pWE->textLength;
  972.     newTextLength = oldTextLength + textLength;
  973.  
  974.     // { lengthen the raw text handle }
  975.     SetHandleSize(pWE->hText, newTextLength);
  976.     err = MemError();
  977.     if (err != noErr) 
  978.         goto cleanup2;
  979.     
  980.     // { calculate ptr to insertion point }
  981.     pInsPoint = (long)(*pWE->hText) + offset;
  982.  
  983.     // { make room for the new text }
  984.     BlockMoveData((Ptr)pInsPoint, (Ptr)(pInsPoint + textLength), oldTextLength - offset);
  985.  
  986.     // { insert new text at the insertion point }
  987.     BlockMoveData(textPtr, (Ptr)pInsPoint, textLength);
  988.  
  989.     // { update the lineStart fields of all lines following the insertion point }
  990.     _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  991.  
  992.     // { update the runStart fields of all style runs following the insertion point }
  993.     _WEBumpRunStart(_WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  994.  
  995.     // { update various fields in the WE record }
  996.     pWE->textLength = newTextLength;
  997.  
  998.     // { make sure the newly inserted text doesn't reference any embedded object }
  999.     pWE->nullStyle.runStyle.tsObject = kNullObject;
  1000.     mode = weDoObject;
  1001.  
  1002.     // { if there is a valid null style, apply it to the newly inserted text }
  1003.     if (BTST(pWE->flags, weFUseNullStyle))
  1004.         mode = mode + (weDoAll + weDoReplaceFace);
  1005.  
  1006.     err = _WESetStyleRange(offset, offset + textLength, mode, &pWE->nullStyle.runStyle, hWE);
  1007.     if (err != noErr)
  1008.     {
  1009.         goto cleanup2;
  1010.     }
  1011.  
  1012. cleanup1:
  1013.     // { clear result code }
  1014.     err = noErr;
  1015.  
  1016. cleanup2:
  1017.     // { return result code }
  1018.     return err;
  1019. } // { _WEInsertText }
  1020.